
#include <algorithm>
#include <sstream>
#include <string>
#include "deformationShape_FSGPU.h"

#include "fbocheck.h"

namespace MLS {
   
///
/// Returns 1 if an OpenGL error occurred, 0 otherwise.
static int gl_error_check() {
   
   GLenum glErr;
   int retCode = 0;
   
   glErr = glGetError();
   while (glErr != GL_NO_ERROR) {
      cerr << "glError : " << gluErrorString(glErr) << "\n";
      retCode = 1;
      glErr = glGetError();
   }
   return retCode;
}

DeformationShape_FSGPU::DeformationShape_FSGPU() {
   Deformation::Deformation();
   
   model = 0;
   shaderFileName = "../mlsrigiddeformer.fs.h";
}

DeformationShape_FSGPU::~DeformationShape_FSGPU() {
   
}
   
void DeformationShape_FSGPU::setting(Shape3D *m, const MLS::Constraints *constraints) {
   
   assert(m != 0);
   model = m;
   
   assert (!gl_error_check());  // testing all ok in opengl machine
   
   // Write the constraints number on the source file
   assert(constraints->size()<1000); /// se for maior ver a definacao do 3define no shader
   ostringstream OcpNumber;
   OcpNumber<<constraints->size();
   
   quadSize = (uint)(std::ceil(std::sqrt(model->nvertices())));
   
   string sdefNconstraints; 
   sdefNconstraints = "#define n " + OcpNumber.str() + " ";
   sdefNconstraints.resize(14,' ');
   
   assert(not shaderFileName.empty());
   ofstream fileout(shaderFileName.c_str(), ios::in | ios::out | ios::binary);
   assert(fileout);
   fileout.seekp(0, ios::beg);
   fileout<<sdefNconstraints;
   fileout.close();
   
   assert(!gl_error_check());  // testing all ok
   kernel.fragment_source(shaderFileName.c_str());
   kernel.install(true);
   
   setFBO();
   
   glActiveTexture(GL_TEXTURE7);
   glGenTextures(1, &texPpoints);
   glBindTexture(GL_TEXTURE_RECTANGLE_NV, texPpoints);
   glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_FLOAT_RGB32_NV, 1, constraints->size(), 0, 
                GL_RGB, GL_FLOAT, &constraints->positions[0][0]);
   //~ checkGLerrors("os P points");
   assert (!gl_error_check());  // testing all ok
   
   glActiveTexture(GL_TEXTURE8);
   glGenTextures(1, &texQpoints);
   glBindTexture(GL_TEXTURE_RECTANGLE_NV, texQpoints);
   glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_RECTANGLE_NV, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, GL_FLOAT_RGB32_NV, 1, constraints->size(), 0, 
                GL_RGB, GL_FLOAT, &constraints->positions[0][0]);
   //~ checkGLerrors("os Q points");
   assert (!gl_error_check());  // testing all ok
   
   glActiveTexture(GL_TEXTURE9);
   glGenTextures(1, &texModelPositions);
   glBindTexture(GL_TEXTURE_2D, texModelPositions);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F_ARB, quadSize, quadSize, 0, 
                GL_RGB, GL_FLOAT, model->vpositions);
   
   kernel.use(true);
   kernel.set_uniform("P", 7);
   kernel.set_uniform("Q", 8);
   kernel.set_uniform("Positions", 9);
   kernel.use(false);
   //~ checkGLerrors("saindo dos settings");
   assert (!gl_error_check());  // testing all ok
}

void DeformationShape_FSGPU::setFBO() {
   
   /// ////////////////////////////////////////////////////////////////////////
   /// make two object textures for FBO
   glGenTextures(2, texFBO);
   /// initialize texture that will store the framebuffer image
   glActiveTexture(GL_TEXTURE1);
   glBindTexture(GL_TEXTURE_2D, texFBO[0]);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, quadSize, quadSize, 0, 
                GL_RGBA, GL_FLOAT, 0);
   //~ checkGLerrors("textura 1 para o FBO");
   
   glBindTexture(GL_TEXTURE_2D, texFBO[1]);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, quadSize, quadSize, 0, 
                GL_RGBA, GL_FLOAT, 0);
   //~ checkGLerrors("textura 2 para o FBO");
   
   /// //////////////////////////////////////////////////////////////////////
   glGenFramebuffersEXT(1, &fbo);
   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 
                             GL_TEXTURE_2D, texFBO[0], 0);
   
   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, 
                             GL_TEXTURE_2D, texFBO[1], 0);
   
   //see if everything is OK
   CHECK_FRAMEBUFFER_STATUS()
   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
   glClearColor(0.0,0.0,0.0,1.0);
   glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
   glClear(GL_COLOR_BUFFER_BIT);
   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
   glClearColor(1.0,1.0,1.0,1.0);
}

void DeformationShape_FSGPU::execute_deformation(const MLS::Constraints *constraints) {
   
   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
   kernel.use(true);
   
   ///< It transfers the Q positions to the GPU memory
   glActiveTexture(GL_TEXTURE4);
   glBindTexture(GL_TEXTURE_RECTANGLE_NV, texQpoints);
   glTexSubImage2D(GL_TEXTURE_RECTANGLE_NV,0,0,0,1,
                   constraints->size(),GL_RGB,GL_FLOAT,&constraints->positions[0][0]);
   //~ checkGLerrors("sending the Q positions to GPU");
   //~ kernel_mlsrigid.set_uniform("Q", 4);
   
   glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
   //~ glDrawBuffers(2, fbo_buffers_1);
   
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluOrtho2D(0.0, quadSize, 0.0, quadSize);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glViewport(0, 0, (GLsizei) quadSize, (GLsizei) quadSize);
   
   glBegin(GL_QUADS);
      glTexCoord2d(0,0);        glVertex2f(0, 0);
      glTexCoord2d(1.0,0);      glVertex2f(quadSize, 0);
      glTexCoord2d(1.0,1.0);    glVertex2f(quadSize, quadSize);
      glTexCoord2d(0,1.0);      glVertex2f(0, quadSize);
   glEnd();
   
   glFinish();
   
   glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
   glReadPixels(0, 0, quadSize, quadSize, GL_RGB, GL_FLOAT, model->vpositions);
   
   kernel.use(false);
   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
   
   //~ glPopAttrib();
   /// The viewport modelview/projection matrix must be newly sets by the main program !
}

} // end MLS namespace
